<?php
define('FRAME_ROOT_PATH', dirname(__FILE__));

require_once FRAME_ROOT_PATH . '/Exception/RouteException.php';
require_once FRAME_ROOT_PATH . '/Request.php';

/**
 * 路由对象
 * 实现解析用户请求，加载request对象
 *
 * @author RD
 */
class Route
{
    private $_get = array();
    private $_post = array();
    private $_cookie = array();
    private $_server = array();
    private $_request = array();
    private $_controllerPath = NULL;
    private $_config = array('defaultModule' => 'default' , 'defaultController' => 'index' , 'defaultAction' => 'index');
    private $_controllerSets;
    private $_module;
    private $_controller;
    private $_action;
    private $_host;
    private $_baseUrl;
    private $_moduleKey = 'module';
    private $_controllerKey = 'controller';
    private $_actionKey = 'action';
    private $_customKey = 'custom';
    private $_isRewrite;
    private $_personalSets;

    /**
     * 初始化
     * @param <String|Array> $controllerSets array ('moduleName' => $controllerPath);
     * @param <Array> $sets 自定义路由 array ('name' => array ('module' => '', 'controller' => '', 'action' => '')
     * @param <boolean> $isRewrite 是否开启rewrite路径解析，以及输出
     */
    public function __construct ($controllerSets, Array $sets = array (), $isRewrite = false)
    {
        if (is_string($controllerSets))
        {
            $this->_controllerSets = array('default' => $controllerSets);
        }
        else
        {
            $this->_controllerSets = $controllerSets;
        }
        $this->_isRewrite = $isRewrite;
        $this->_personalSets = $sets;
        
        $this->_doInit();
    }

    /**
     * 初始化系统运行环境
     */
    private function _doInit ()
    {
        //strip magic quotes
        if (get_magic_quotes_gpc())
        {
            $_GET=$this->_stripSlashes($_GET);
            $_POST=$this->_stripSlashes($_POST);
            $_COOKIE=$this->_stripSlashes($_COOKIE);
            $_SERVER=$this->_stripSlashes($_SERVER);
            $_FILES=$this->_stripSlashes($_FILES);
        }
        $this->_get = &$_GET;
        $this->_post = &$_POST;
        $this->_cookie = &$_COOKIE;
        $this->_request = array_merge($_GET, $_POST);
        $this->_server = &$_SERVER;
        //命令行模式
        if (isset($argc))
        {
            $this->_request['argc'] = $argc;
            $this->_request = array_merge($this->_request, $argv);
        }
    }

    /**
     * 设置参数
     *
     * @param $key 键名
     * @param $value 值
     * @return void
     */
    public function setParam ($key, $value)
    {
        $this->_request[$key] = $value;
    }

    /**
     * 获取参数 (GET POST)
     *
     * @param $key 键名
     * @param $defaultValue 默认值
     * @return mixed value
     */
    public function getParam ($key, $defaultValue = NULL)
    {
        if (isset($this->_request[$key]))
        {
            return $this->_request[$key];
        }
        else
        {
            return $defaultValue;
        }
    }

    /**
     * 获取全部参数
     *
     * @return array
     */
    public function getAllParams ()
    {
        return $this->_request;
    }

    /**
     * 获取POST参数
     *
     * @param $key 键名
     * @param $defaultValue 默认值
     * @return mixed value
     */
    public function getPost ($key = NULL, $defaultValue = NULL)
    {
        if ($key == NULL)
            return $this->_post;
        
        if (isset($this->_post[$key]))
        {
            return $this->_post[$key];
        }
        else
        {
            return $defaultValue;
        }
    }

    /**
     * 获取SERVER参数
     *
     * @param $key 键名
     * @param $defaultValue 默认值
     * @return mixed value
     */
    public function getServer ($key = NULL, $defaultValue = NULL)
    {
        if ($key == NULL)
            return $this->_server;
        
        if (isset($this->_server[$key]))
        {
            return $this->_server[$key];
        }
        else
        {
            return $defaultValue;
        }
    }

    /**
     * 获取COOKIE参数
     *
     * @param $key 键名
     * @param $defaultValue 默认值
     * @return mixed value
     */
    public function getCookie ($key = NULL, $defaultValue = NULL)
    {
        if ($key == NULL)
            return $this->_cookie;
        
        if (isset($this->_cookie[$key]))
        {
            return $this->_cookie[$key];
        }
        else
        {
            return $defaultValue;
        }
    }

    /**
     *获取模块名称
     * @return <String> 模块名称
     */
    public function getModule ()
    {
        return $this->_module;
    }

    /**
     *
     * @return <String> 控制器路径
     */
    public function getControllerPath ()
    {
        return $this->_controllerPath;
    }

    /**
     *
     * @return <String> 相对路径
     */
    public function getBaseUrl ()
    {
        return $this->_baseUrl;
    }

    /**
     *
     * @return <String> 获取请求主机名称
     */
    public function getHost ()
    {
        return $this->_host;
    }

    /**
     *
     * @return <String> 控制器名称
     */
    public function getController ()
    {
        return $this->_controller;
    }

    /**
     *
     * @return <String> 方法名称
     */
    public function getAction ()
    {
        return $this->_action;
    }

    /**
     *
     * @return <boolean> 是否开启rewrite
     */
    public function getisRewrite ()
    {
        return $this->_isRewrite;
    }

    /**
     *
     * @return <Array> 返回自定义的路由
     */
    public function getPersonalSets ()
    {
        return $this->_personalSets;
    }

    /**
     * 设置自定义的路由解析模块名称
     *
     * @param $key 路由解析模块名称
     * @return Route
     * @author RD
     */
    public function setModuleKey ($key)
    {
        $this->_moduleKey = $key;
        
        return $this;
    }

    /**
     * 设置自定义的路由解析控制器名称
     *
     * @param $key 路由解析制器名称
     * @return Route
     * @author RD
     */
    public function setControllerKey ($key)
    {
        $this->_controllerKey = $key;
        
        return $this;
    }

    /**
     * 设置自定义的动作名称
     *
     * @param $key 动作名称
     * @return Route
     * @author RD
     */
    public function setActionKey ($key)
    {
        $this->_actionKey = $key;
        
        return $this;
    }
    
    
    /**
     * 设置自定义的变量名称（非rewrite模式使用）
     *
     * @param $key 动作名称
     * @return Route
     * @author RD
     */
    public function setCustomKey ($key)
    {
        $this->_customKey = $key;
        
        return $this;
    }

    /**
     * 获取路由解析模块名称
     *
     * @return String key
     * @author RD
     */
    public function getModuleKey ()
    {
        return $this->_moduleKey;
    }

    /**
     * 获取路由解析控制器名称
     *
     * @return String key
     * @author RD
     */
    public function getControllerKey ()
    {
        return $this->_controllerKey;
    }

    /**
     * 获取路由解析动作名称
     *
     * @return String key
     * @author RD
     */
    public function getActionKey ()
    {
        return $this->_actionKey;
    }


    /**
     * 获取自定义的变量名称（非rewrite模式使用）
     *
     * @param $key 动作名称
     * @return NULL
     * @author RD
     */
    public function getCustomKey ()
    {
        return $this->_customKey;
    }
    
    
    /**
     * 解析参数，解析路由
     */
    private function _parseRoute ()
    {
        $REQUEST_URI = $this->getServer('REQUEST_URI');
        $SCRIPT_NAME = $this->getServer('SCRIPT_NAME');
        
        if ($REQUEST_URI == NULL || $SCRIPT_NAME == NULL)
        {
            throw new RouteException('Server Variable REQUEST_URI And SCRIPT_NAME is NULL');
        }
        else
        {
            //相对地址解析
            $this->_baseUrl = dirname($SCRIPT_NAME);
            $this->_baseUrl = str_replace('\\', '/', $this->_baseUrl);
            $this->_host = 'http://' . $this->getServer('HTTP_HOST');
            //把前缀相对路径去掉
            if ($this->_baseUrl == '/')
            {
                $this->_baseUrl = '';
            }
            else
            {
                $REQUEST_URI = str_replace($this->_baseUrl, '', $REQUEST_URI);
            }
            $temp_len = strlen($REQUEST_URI);
			//去除URI尾部的'/'
            $REQUEST_URI = rtrim($REQUEST_URI,'\/');
            $result = array();
            if ($this->_isRewrite)
            {
                $result = $this->_parseRewriteRoute($REQUEST_URI);
            }
            else
            {
                $result = $this->_parseCommonRoute($REQUEST_URI);
            }
            
            $this->_module = strtolower($result['module']);
            $this->_controller = ucfirst(strtolower($result['controller']));
            $this->_action = strtolower($result['action']);
            
            $this->_request = array_merge($this->_request, $result['param']);
            
            $this->_request['module'] = $this->_module;
            $this->_request['controller'] = strtolower($this->_controller);
            $this->_request['action'] = $this->_action;
            
            if (isset ($this->_controllerSets[$this->_module])) {
                $this->_controllerPath = $this->_controllerSets[$this->_module];
            } else {
                throw new RouteException ( 'Module Not Found:' . $this->_module);
            }
        }
    }

    /**
     * 解析rewrite情况下的路由
     * 支持格式为： 1，单个参数：(解析顺序为)
     *                  1.1：自定义路由名    = 自定义路由设置的参数
     *                  1.2：modulename      = modulename/defaultController/defaultAction
     *                  1.3：controllername  = defaultModule/controllername/defaultAction
     *              2，两个参数：
     *                  2.1：modulename/controllername  = modulename/controllername/defaultAction
     *                  2.2：controllername/actionname  = defaultModule/controllername/actionname
     *              3，三个参数：
     *                  3.1：自定义路由名/param1/value1             = 自定义路由设置的参数 & param
     *                  3.2：modulename/controllername/actionname   = modulename/controllername/actionname
     *              4，三个参数以上：
     *                  4.1：自定义路由名/param1/value1/param2/value2           = 自定义路由设置的参数 & param
     *                  4.2：modulename/controllername/actionname/param1/value1 = modulename/controllername/actionname & param
     *                  4.3：controllername/actionname/param1/value1            = defaultModule/controllername/actionname & param
     *
     * @param <String> $requestUri 处理过的访问地址
     *
     * @return array ('controller' => '', 'action' => '', 'module'=> '', 'param' =>array())
     */
    private function _parseRewriteRoute ($requestUri)
    {
        $result = array('controller' => $this->_config['defaultController'] , 'action' => $this->_config['defaultAction'] , 'module' => $this->_config['defaultModule'] , 'param' => array());
        
        $result['param']['__url__'] = 'R';
        
        //去掉结尾的查询参数 (?a=c&b=d)
        $dopost = stripos($requestUri, '?');
        $dopost > 0 && $requestUri = substr($requestUri, 0, $dopost);
        
        if ( strlen ($requestUri) > 0 && $requestUri[0] == '/')
        {
            $requestUri = substr($requestUri, 1);
        }
        
        $arr = array();
        $requestUri != '' && $arr = explode('/', $requestUri);
        
        $arr_size = count($arr);
        
        if ($arr_size == 0)
        {
            $result['param']['__url__'] .= '0';
        
        }
        elseif ($arr_size == 1)
        {
            //1.1 自定义路由名    = 自定义路由设置的参数
            if (isset($this->_personalSets[$arr[0]]))
            {
                $result['module'] = $this->_personalSets[$arr[0]]['module'];
                $result['controller'] = $this->_personalSets[$arr[0]]['controller'];
                $result['action'] = $this->_personalSets[$arr[0]]['action'];
                $result['param']['__url__'] .= '1.1';
            }
            elseif (isset($this->_controllerSets[$arr[0]]))
            {
                //1.2 modulename      = modulename/defaultController/defaultAction
                $result['module'] = $arr[0];
                $result['param']['__url__'] .= '1.2';
            
            }
            else
            {
                //1.3 controllername  = defaultModule/controllername/defaultAction
                $result['controller'] = $arr[0];
                $result['param']['__url__'] .= '1.3';
            
            }
        
        }
        else if ($arr_size == 2)
        {
            
            if (isset($this->_controllerSets[$arr[0]]))
            {
                //2.1 modulename/controllername  = modulename/controllername/defaultAction
                $result['module'] = $arr[0];
                $result['controller'] = $arr[1];
                $result['param']['__url__'] .= '2.1';
            
            }
            else
            {
                //2.2 controllername/actionname  = defaultModule/controllername/actionname
                $result['controller'] = $arr[0];
                $result['action'] = $arr[1];
                $result['param']['__url__'] .= '2.2';
            
            }
        }
        else if ($arr_size == 3)
        {
            
            //3.1 自定义路由名/param1/value1             = 自定义路由设置的参数 & param
            if (isset($this->_personalSets[$arr[0]]))
            {
                $result['module'] = $this->_personalSets[$arr[0]]['module'];
                $result['controller'] = $this->_personalSets[$arr[0]]['controller'];
                $result['action'] = $this->_personalSets[$arr[0]]['action'];
                
                $result['param'][$arr[1]] = $arr[2];
                $result['param']['__url__'] .= '3.1';
            
            }
            else
            {
                //3.2 modulename/controllername/actionname   = modulename/controllername/actionname
                $result['module'] = $arr[0];
                $result['controller'] = $arr[1];
                $result['action'] = $arr[2];
                $result['param']['__url__'] .= '3.2';
            
            }
        }
        else
        {
            $startParam = 0;
            //4.1：自定义路由名/param1/value1/param2/value2           = 自定义路由设置的参数 & param
            if (isset($this->_personalSets[$arr[0]]))
            {
                $result['module'] = $this->_personalSets[$arr[0]]['module'];
                $result['controller'] = $this->_personalSets[$arr[0]]['controller'];
                $result['action'] = $this->_personalSets[$arr[0]]['action'];
                
                $startParam = 1;
                $result['param']['__url__'] .= '4.1';
            
            }
            else if (isset($this->_controllerSets[$arr[0]]))
            {
                //4.2：modulename/controllername/actionname/param1/value1 = modulename/controllername/actionname & param
                $result['module'] = $arr[0];
                $result['controller'] = $arr[1];
                $result['action'] = $arr[2];
                
                $startParam = 3;
                
                $result['param']['__url__'] .= '4.2';
            
            }
            else
            {
                //4.3：controllername/actionname/param1/value1            = defaultModule/controllername/actionname & param
                $result['controller'] = $arr[0];
                $result['action'] = $arr[1];
                
                $startParam = 2;
                $result['param']['__url__'] .= '4.3';
            
            }
            
            $arr_size --;
            for (; $startParam < $arr_size; $startParam += 2)
            {
                $result['param'][$arr[$startParam]] = urldecode ($arr[$startParam + 1]);
            }
        }
        
        return $result;
    }

    /**
     * 解析无rewrite情况下的路由
     * 通过参数解析,如果相应参数没有，则用默认值代替
     * @param <String> $requestUri 处理过的访问地址
     *
     * @return array ('controller' => '', 'action' => '', 'module'=> '', 'param' =>array())
     */
    private function _parseCommonRoute ($requestUri)
    {
        $result = array('controller' => $this->_config['defaultController'] , 'action' => $this->_config['defaultAction'] , 'module' => $this->_config['defaultModule'] , 'param' => array());
        $result['param']['__url__'] = 'C';
        
        if ($this->getParam ($this->_customKey) != NULL) {
            $customV = $this->getParam ($this->_customKey);

            if (isset ($this->_personalSets[$customV])) {
                $result['module']       = $this->_personalSets[$customV]['module'];
                $result['controller']   = $this->_personalSets[$customV]['controller'];
                $result['action']       = $this->_personalSets[$customV]['action'];
                $result['param']['__url__'] .= '1';
            }
            
            return $result;
        }
        
        
        if ($this->getParam($this->_moduleKey) != NULL)
        {
            $result['module'] = $this->getParam($this->_moduleKey);
            $result['param']['__url__'] .= 'm';
        }
        
        if ($this->getParam($this->_controllerKey) != NULL)
        {
            $result['controller'] = $this->getParam($this->_controllerKey);
            $result['param']['__url__'] .= 'c';
        }
        
        if ($this->getParam($this->_actionKey) != NULL)
        {
            $result['action'] = $this->getParam($this->_actionKey);
            $result['param']['__url__'] .= 'a';
        }
        
        return $result;
    }

    /**
     * 派发运行
     */
    private function _disPatch ($controller, $controllerPath, $action)
    {
        $c = $controller . 'Controller';
        if (! file_exists($controllerPath . '/' . $c . '.php'))
            throw new RouteException('File Not Found:' . $controllerPath . '/' . $c . '.php');
        else
        {
            require_once $controllerPath . '/' . $c . '.php';
            if (! class_exists($c))
            {
                throw new RouteException('Controller Not Found:' . $c);
            }
            else
            {
                $request = new $c($this);
                if (! method_exists($request, $action . 'Action'))
                {
                    throw new RouteException('Action Not Found:' . $action . 'Action');
                }
                else
                {
                    $request->doRequest();
                }
            }
        }
    }


    /**
     * 去掉转义字符
     * @param $v 
     */

    public function _stripSlashes ($v)
    {
        if (is_array($v))
        {
            foreach ($v as $key => $val)
            {
                $v[$key] = $this->_stripSlashes($val);
            }
        }
        else
        {
            $v = strtr ($v, array ("\\\\" => "\\", "\\\""=>'"', "\\'" => "'"));
        }
        return $v;
    }

    /**
     * 运行程序
     * @param <boolean> $isThrowException 是否抛出异常
     */
    public function run ($isThrowException = true)
    {
        $this->_parseRoute();
        $this->_disPatch($this->_controller, $this->_controllerPath, $this->_action);
    }
}